using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Zsa.Emulation.Mappers;

namespace Zsa.Emulation.Cartridge
{

    /// <summary>
    /// Contains all the information need to read a cartridge.
    /// </summary>
    [Serializable()]
    public class CartridgeFileInfo
    {
        #region Fields
        /// <summary>
        /// The roms full path.  This will just be a
        /// file name if the rom does not exist.
        /// </summary>
        private string _FileName;

        /// <summary>
        /// The format that the cartridge is in.
        /// </summary>
        private string _FormatName;

        /// <summary>
        /// The file hash for the rom.
        /// </summary>
        private Guid _FileHash;

        /// <summary>
        /// The size in bytes of the cartridge.
        /// </summary>
        private int _FileSize; 
        
        /// <summary>
        /// The offsets for the program rom bank.
        /// </summary>
        private int[] _PrgRomBankOffsets;


        /// <summary>
        /// The char rom bank offsets.
        /// </summary>
        private int[] _ChrRomBankOffsets;

        /// <summary>
        /// The number of ram banks.  These are not 
        /// stored with the rom file.
        /// </summary>
        private int _RamBankCount;

        /// <summary>
        /// The rom flags.
        /// </summary>
        private RomFlags _RomFlags;

        /// <summary>
        /// The cart mapper information.
        /// </summary>
        private MapperInformation _MapperInfo;
        #endregion Fields

        #region Constructor
        /// <summary>
        /// Initializes a new instance of the <see cref="T:CartridgeFileInfo"/> class.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <param name="formatName">Name of the format.</param>
        /// <param name="fileHash">The file hash.</param>
        /// <param name="fileSize">Size of the file.</param>
        /// <param name="prgRoms">The PRG roms.</param>
        /// <param name="chrRoms">The CHR roms.</param>
        /// <param name="ramBankCount">The ram bank count.</param>
        /// <param name="romFlags">The rom flags.</param>
        /// <param name="mapperInfo">The mapper info.</param>
        internal CartridgeFileInfo(string fileName, string formatName,
            Guid fileHash, int fileSize, int[] prgRoms, int[] chrRoms, int ramBankCount,
            RomFlags romFlags, MapperInformation mapperInfo)
        {
            // check the args.
            if (string.IsNullOrEmpty(fileName))
            {
                throw new ArgumentException("The file name is invalid !","fileName");
            }
            if (string.IsNullOrEmpty(formatName))
            {
                throw new ArgumentException("The format Name is invalid !", "formatName");
            }
            //if (fileHash == Guid.Empty)
            //{
            //    throw new ArgumentException("fileHash", "The file hash is invalid !");
            //}
            if (prgRoms == null || prgRoms.Length < 1)
            {
                throw new ArgumentException("prgRoms", "There must be at least one program rom !");
            }
                        

            _FileName = fileName;
            _FormatName = formatName;
            _FileHash = fileHash;
            _RamBankCount = ramBankCount;
            _RomFlags = romFlags;
            _PrgRomBankOffsets = prgRoms;
            _ChrRomBankOffsets = chrRoms == null ? new int[0] : chrRoms;
            _MapperInfo = mapperInfo;
        }





        /// <summary>
        /// Initializes a new instance of the <see cref="CartInfo"/> class.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <param name="formatName">Name of the format.</param>
        /// <param name="hash">The hash.</param>
        /// <param name="mapperType">Type of the mapper.</param>
        /// <param name="romBankCount">The rom bank count.</param>
        /// <param name="vromBankCount">The vrom bank count.</param>
        /// <param name="ramBankCount">The ram bank count.</param>
        /// <param name="flags">The flags.</param>
        /// <param name="startOffset">The start offset.</param>
        /// <param name="vromStartOffset">The vrom start offset.</param>
        internal CartridgeFileInfo(
            string fileName, string formatName, Guid hash, int fileSize,
            MapperInformation mapperInfo, int prgRomBankCount, int chrBankCount, 
            int ramBankCount, RomFlags flags,int prgRomStartOffset, 
            int chrStartOffset, int chrBankSize, int prgBankSize)
            : this ( fileName, formatName, hash, fileSize, CreateSequentialOffsets(prgRomStartOffset, prgBankSize, prgRomBankCount),
                CreateSequentialOffsets(chrStartOffset, chrBankSize, chrBankCount), ramBankCount,
                    flags,mapperInfo)
        {
            
            if (fileName == null)
            {
                throw new ArgumentNullException("fileName", "The filename cannot be null !");
            }
            

        }


        #endregion Constructor

        #region Properties
        /// <summary>
        /// Gets the ram bank count.
        /// </summary>
        /// <value>The ram bank count.</value>
        public int RamBankCount
        {
            get
            {
                return _RamBankCount;
            }
        }

        /// <summary>
        /// Gets the rom bank count.
        /// </summary>
        /// <value>The rom bank count.</value>
        public int PrgRomBankCount
        {
            get
            {
                return _PrgRomBankOffsets.Length;
            }
        }

        /// <summary>
        /// Gets the VROM bank count.
        /// </summary>
        /// <value>The V rom bank count.</value>
        public int ChrRomBankCount
        {
            get 
            {
                return _ChrRomBankOffsets.Length;
            }
        }

        /// <summary>
        /// Gets the rom hash.
        /// </summary>
        /// <value>The rom hash.</value>
        public Guid RomHash
        {
            get
            {
                return _FileHash;
            }
        }

        /// <summary>
        /// Gets the name of the format.
        /// </summary>
        /// <value>The name of the format.</value>
        public string FormatName
        {
            get
            {
                return _FormatName;
            }
        }

        /// <summary>
        /// Gets a value indicating whether [use vertical mirroring].
        /// </summary>
        /// <value>
        /// 	<c>true</c> if [use vertical mirroring]; otherwise, <c>false</c>.
        /// </value>
        public bool UseVerticalMirroring
        {
            get
            {
                return (_RomFlags & RomFlags.VerticalMirroring) == RomFlags.VerticalMirroring;
            }
        }

        /// <summary>
        /// Gets a value indicating whether [use four screen mirroring].
        /// </summary>
        /// <value>
        /// 	<c>true</c> if [use four screen mirroring]; otherwise, <c>false</c>.
        /// </value>
        public bool UseFourScreenMirroring
        {
            get
            {
                return (_RomFlags & RomFlags.FourScreenMirroring) == RomFlags.FourScreenMirroring;
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance has battery pack ram.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance has battery pack ram; otherwise, <c>false</c>.
        /// </value>
        public bool HasBatteryPackRam
        {
            get
            {
                return (_RomFlags & RomFlags.BatteryPackRam) == RomFlags.BatteryPackRam;
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance has trainer.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance has trainer; otherwise, <c>false</c>.
        /// </value>
        public bool HasTrainer
        {
            get
            {
                return (_RomFlags & RomFlags.HasTrainer) == RomFlags.HasTrainer;
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance is pal.
        /// </summary>
        /// <value><c>true</c> if this instance is pal; otherwise, <c>false</c>.</value>
        public bool IsPal
        {
            get
            {
                return (_RomFlags & RomFlags.IsPal) == RomFlags.IsPal;
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance has VS cart.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance has VS cart; otherwise, <c>false</c>.
        /// </value>
        public bool HasVSCart
        {
            get
            {
                return (_RomFlags & RomFlags.VSCart) == RomFlags.VSCart;
            }
        }

        /// <summary>
        /// Gets the type of the mapper.
        /// </summary>
        /// <value>The type of the mapper.</value>
        public MapperInformation MapperInformation
        {
            get
            {
                return _MapperInfo;
            }
        }

        /// <summary>
        /// Gets the name of the file.
        /// </summary>
        /// <value>The name of the file.</value>
        public string FileName
        {
            get
            {
                return Path.GetFileName(_FileName);
            }
        }

        /// <summary>
        /// Gets the path to the file.
        /// </summary>
        public string FilePath
        {
            get
            {
                if ( Path.IsPathRooted(_FileName) )
                {
                    return Path.GetDirectoryName(_FileName);
                }
                return string.Empty;
            }
        }
        /// <summary>
        /// Gets the file path.
        /// </summary>
        /// <value>The file path.</value>
        public string FullPath
        {
            get
            {
                return _FileName;
            }
        }

        /// <summary>
        /// Gets a value indicating whether [rom exits].
        /// </summary>
        /// <value><c>true</c> if [rom exits]; otherwise, <c>false</c>.</value>
        public bool RomExits
        {
            get
            {
                if (_FileName == null)
                {
                    return false;
                }
                return File.Exists(_FileName);
            }
        }


        /// <summary>
        /// Gets the total size of the program rom byte.
        /// </summary>
        /// <value>The total size of the program rom byte.</value>
        public int TotalProgramRomByteSize
        {
            get
            {
                return NesMemoryConstants.PrgRomBankByteSize * PrgRomBankCount;
            }
        }

        /// <summary>
        /// Gets the total size of the video rom byte.
        /// </summary>
        /// <value>The total size of the video rom byte.</value>
        public int TotalVideoRomByteSize
        {
            get
            {
                return (PPU.VideoMemoryConstants.PatternTableByteSize*2) * ChrRomBankCount;
            }
        }


        /// <summary>
        /// Gets the PRG rom bank offsets.
        /// </summary>
        /// <value>The PRG rom bank offsets.</value>
        public int[] PrgRomBankOffsets
        {
            get { return _PrgRomBankOffsets.Clone() as int[]; }

        }
        /// <summary>
        /// Gets the CHR rom bank offsets.
        /// </summary>
        /// <value>The CHR rom bank offsets.</value>
        public int[] ChrRomBankOffsets
        {
            get { return _ChrRomBankOffsets.Clone() as int[]; }
        }
        #endregion Properties

        #region Methods

        /// <summary>
        /// Gets the offsets for the filestream 
        /// </summary>
        /// <param name="bankType"></param>
        /// <param name="bankNumber"></param>
        /// <returns></returns>
        public virtual int GetOffset(RomBankType bankType, int bankNumber)
        {
            // Clear all the flags except video or program
            bankType &= RomBankType.VideoRom | RomBankType.ProgramRom;
            // Get the offset.
            if (bankType == RomBankType.ProgramRom)
            {
                if (bankNumber > _PrgRomBankOffsets.Length)
                {
                    throw new ArgumentOutOfRangeException("bankNumber", bankNumber, "The bank number is out of range !");
                }
                return _PrgRomBankOffsets[bankNumber] ;
            }
            else if (bankType == RomBankType.VideoRom)
            {
                if (bankNumber > _ChrRomBankOffsets.Length)
                {
                    throw new ArgumentOutOfRangeException("bankNumber", bankNumber, "The bank number is out of range !");
                }
                return _ChrRomBankOffsets[bankNumber];
            }
            else
            {
                throw new ArgumentException("Invalid bank type !", "bankType");
            }
        }

        /// <summary>
        /// Creates the sequential offsets.
        /// </summary>
        /// <param name="startOffset">The start offset.</param>
        /// <param name="size">The size.</param>
        /// <param name="count">The count.</param>
        /// <returns></returns>
        public static int[] CreateSequentialOffsets(int startOffset, int size,
            int count)
        {
            int[] _Offsets = new int[count];
            for (int _TempIndex = 0; _TempIndex < _Offsets.Length; _TempIndex++)
            {
                _Offsets[_TempIndex] = startOffset;
                startOffset += size;
            }
            return _Offsets;
        }

        ///// <summary>
        ///// Opens a read only file stream to the cartridge.
        ///// </summary>
        ///// <returns></returns>
        //public FileStream OpenStream()
        //{
        //    if (_FileName == null)
        //    {
        //        throw new ArgumentNullException(
        //            "fileName",
        //            "The file name argument cannot be null !"
        //            );
        //    }
        //    if (_FileName == string.Empty)
        //    {
        //        throw new ArgumentException(
        //            "The file name argument cannot be null !",
        //            "fileName"
        //            );
        //    }
        //    return _FileStream =
        //        new FileStream(
        //            _FileName,
        //            FileMode.Open,
        //            FileAccess.Read,
        //            FileShare.Read
        //        );
        //}

        //public Cart OpenCart()
        //{

        //    throw new NotImplementedException();
        //}



        /// <summary>
        /// Returns a <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
        /// </returns>
        public override string ToString()
        {
            StringBuilder _Builder = new StringBuilder();
            _Builder.AppendFormat("File : {0}\r\n", Path.GetFileNameWithoutExtension(_FileName));
            _Builder.AppendFormat("Hash : {0}\r\n", _FileHash.ToString());
            _Builder.AppendLine("---------------------------------");
            _Builder.AppendFormat("Is Pal : {0}\r\n", IsPal.ToString());
            //_Builder.AppendFormat("Mapper : {0}\r\n", _MapperType.ToString());
            //_Builder.AppendFormat("Prg-Rom Bank Count : {0}\r\n", _PrgRomBankCount.ToString());
            //_Builder.AppendFormat("V-Rom Bank Count : {0}\r\n", _ChrRomBankCount.ToString());
            _Builder.AppendFormat("Battery Ram Bank Count : {0}\r\n", _RamBankCount.ToString());
            return _Builder.ToString();

        }


        #endregion Methods
    }
}

